package xfuzzy.alg_genetico.indeterminado.view;


import xfuzzy.util.*;
import xfuzzy.xfsl.view.Xfsl;
import xfuzzy.xfsl.view.XfslGraphPoint;

import javax.swing.*;
import java.awt.*;

/**
 * Panel de representacion de la evolucion del aprendizaje
 * 
 * @author Francisco Jose Moreno Velo
 * 
 */
public class MyGraphPanel extends JPanel {

	// ----------------------------------------------------------------------------//
	// COSTANTES PRIVADAS //
	// ----------------------------------------------------------------------------//

	/**
	 * Codigo asociado a la clase serializable
	 */
	private static final long serialVersionUID = 95505666603068L;

	/**
	 * Altura del panel
	 */
	private final static int HEIGHT = 300;

	/**
	 * Factor de cambio de base de los algoritmos
	 */
	private final static double log10 = Math.log(10.0);

	// ----------------------------------------------------------------------------//
	// MIEMBROS PRIVADOS //
	// ----------------------------------------------------------------------------//

	private Graphics2D gc, gd;
	private Graphics2D gtrn_error/*, gtrn_rmse, gtrn_mxae*/;
	private Graphics2D gtst_error/*, gtst_rmse, gtst_mxae*/;
	private Dimension size;
	private int x0, y0, x1, y1;
	private int yresc = 3;
	private int xresc = 0;
	private int xscale = 20;
	private int pos = 0;
	//private XfslStatus[] hist = new XfslStatus[130];
	private double [] hist;
	//private Xfsl xfsl;
	//private boolean classif;

	// ----------------------------------------------------------------------------//
	// CONSTRUCTOR //
	// ----------------------------------------------------------------------------//

	/**
	 * Constructor
	 */
	public MyGraphPanel(Xfsl xfsl) {
		super();
		Dimension prefsize = new Dimension(getPreferredSize().width, HEIGHT);
		setPreferredSize(prefsize);
		setBackground(XConstants.textbackground);
		setBorder(BorderFactory.createLoweredBevelBorder());
		this.hist = new double[300];
	}

	// ----------------------------------------------------------------------------//
	// M�TODOS P�BLICOS //
	// ----------------------------------------------------------------------------//

	/**
	 * Limpiar la representacion
	 */
	public void reset() {
		yresc = 3;
		xresc = 0;
		xscale = 20;
		pos = 0;
		repaint();
	}

	/**
	 * Añadir un dato a la representacion
	 */
	public void addStatus(double status) {
		//int step = xscale / 20;
		//if ((status.epoch % step) != 0)
		//	return;
		paintStatus(status);
		hist[pos] = status;
		pos++;
	}

	/**
	 * Pintar el panel
	 */
	public void paint(Graphics g) {
		super.paint(g);
		setVars(g);
		paintLeyend();
		paintAxis();
		paintHist();
	}

	// ----------------------------------------------------------------------------//
	// M�TODOS PRIVADOS //
	// ----------------------------------------------------------------------------//

	/**
	 * Calcula las variables internas
	 */
	private void setVars(Graphics g) {
		this.gc = (Graphics2D) g;
		this.gtrn_error = (Graphics2D) g.create();
		gtrn_error.setColor(Color.blue);
		//this.gtrn_rmse = (Graphics2D) g.create();
		//gtrn_rmse.setColor(Color.red);
		//this.gtrn_mxae = (Graphics2D) g.create();
		//gtrn_mxae.setColor(Color.green);

		float dash[] = { 4.0f, 4.0f };
		this.gd = (Graphics2D) g.create();
		this.gd.setStroke(new BasicStroke(1.0f, 2, 0, 10.0f, dash, 0.0f));
		this.gtst_error = (Graphics2D) gd.create();
		gtst_error.setColor(Color.blue);
		//this.gtst_rmse = (Graphics2D) gd.create();
		//gtst_rmse.setColor(Color.red);
		//this.gtst_mxae = (Graphics2D) gd.create();
		//gtst_mxae.setColor(Color.green);

		this.size = getSize();
		this.x0 = 170;
		this.y0 = size.height - 50;
		this.x1 = size.width - 110;
		this.y1 = 30;
	}

	/**
	 * Dibuja la leyenda de la representacion grafica
	 */
	private void paintLeyend() {
		//this.classif = xfsl.getConfig().errorfunction.isClassification();
		//if (!classif) {
			//gc.drawString("Training", 15, 40);
			gc.drawString("Error", 20, 60);
			//gc.drawString("RMSE", 20, 80);
			//gc.drawString("MxAE", 20, 100);
			//gc.drawString("Test", 15, 140);
			gc.drawString("Error", 20, 160);
			//gc.drawString("RMSE", 20, 180);
			//gc.drawString("MxAE", 20, 200);
			gtrn_error.drawLine(60, 55, 100, 55);
			//gtrn_rmse.drawLine(60, 75, 100, 75);
			//gtrn_mxae.drawLine(60, 95, 100, 95);
			gtst_error.drawLine(60, 155, 100, 155);
			//gtst_rmse.drawLine(60, 175, 100, 175);
			//gtst_mxae.drawLine(60, 195, 100, 195);
		//} else {
			//gc.drawString("Training", 15, 40);
			//gc.drawString("Error", 20, 60);
			//gc.drawString("Missc.", 20, 80);
			//gc.drawString("Test", 15, 140);
			//gc.drawString("Error", 20, 160);
			//gc.drawString("Missc.", 20, 180);
			//gtrn_error.drawLine(60, 55, 100, 55);
			//gtrn_rmse.drawLine(60, 75, 100, 75);
			//gtst_error.drawLine(60, 155, 100, 155);
			//gtst_rmse.drawLine(60, 175, 100, 175);
		//}
	}

	/**
	 * Dibuja los ejes de la representacion grafica
	 */
	private void paintAxis() {
		gc.drawString("Iteration", size.width - 90, size.height - 20);
		gc.drawLine(x0, y1, x0, y0 + 1);
		gc.drawLine(x0, y0 + 1, x1, y0 + 1);
		gc.drawLine(x1, y1, x1, y0 + 1);
		FontMetrics fm = getFontMetrics(getFont());

		for (int i = 0; i < yresc; i++) {
			int ypos = y1 + (y0 - y1) * i / yresc;
			String label = "1.0" + (i == 0 ? "" : "E-" + i);
			int xpos = fm.stringWidth(label) + 10;
			gc.drawLine(x0 - 4, ypos, x0, ypos);
			gc.drawString(label, x0 - xpos, ypos + 5);
			gd.drawLine(x0, ypos, x1, ypos);
		}
		for (int i = 0; i <= 5; i++) {
			int iter = i * xscale;
			int xpos = x0 + (x1 - x0) * i / 5;
			int spos = fm.stringWidth("" + iter) / 2;
			gc.drawLine(xpos, y0 + 5, xpos, y0 + 1);
			gc.drawString("" + iter, xpos - spos, y0 + 21);
		}
	}

	/**
	 * Dibuja la evolucion historica
	 */
	private void paintHist() {
		XfslGraphPoint p0, p1;
		if (pos == 0)
			return;
		p0 = createGraphPoint(hist[0]);
		for (int i = 1; i < pos; i++) {
			p1 = createGraphPoint(hist[i]);
			paintPoints(p0, p1);
			p0 = p1;
		}
	}

	/**
	 * Dibuja un nuevo punto en la evolucion
	 */
	private void paintStatus(double status) {
		if (pos == 0)
			return;
		setVars(getGraphics());
		XfslGraphPoint p0 = createGraphPoint(hist[pos - 1]);
		XfslGraphPoint p1 = createGraphPoint(status);
		if (verticalOutOfBounds(p0) || verticalOutOfBounds(p1)) {
			yresc++;
			repaint();
		} else if (horizontalOutOfBounds(p1)) {
			rescale();
			repaint();
		} else
			paintPoints(p0, p1);
	}

	/**
	 * Dibuja un segmento en la evolucion
	 */
	private void paintPoints(XfslGraphPoint p0, XfslGraphPoint p1) {
		//if (p0.testing && p1.testing) {
			//if (!classif)
			//	gtst_mxae.drawLine(p0.x, p0.ysm, p1.x, p1.ysm);
			//gtst_rmse.drawLine(p0.x, p0.ysr, p1.x, p1.ysr);
			gtst_error.drawLine(p0.x, p0.yse, p1.x, p1.yse);
		//}
		//if (!classif)
		//	gtrn_mxae.drawLine(p0.x, p0.yrm, p1.x, p1.yrm);
		//gtrn_rmse.drawLine(p0.x, p0.yrr, p1.x, p1.yrr);
		//gtrn_error.drawLine(p0.x, p0.yre, p1.x, p1.yre);
	}

	/**
	 * Aumenta en un grado la escala temporal
	 */
	private void rescale() {
		for (int i = 0; 2 * i < pos && 2 * i < hist.length; i++)
			hist[i] = hist[2 * i];
		pos = pos / 2;
		xresc++;
		xscale = 20;
		for (int i = 0; i < xresc; i++)
			if (i % 3 != 0)
				xscale *= 2;
			else
				xscale = xscale * 5 / 2;
	}

	/**
	 * Construye un punto de la evolucion del aprendizaje
	 */
	private XfslGraphPoint createGraphPoint(double status) {
		XfslGraphPoint p = new XfslGraphPoint();
		//p.x = (int) (x0 + (x1 - x0) * status.epoch / (5 * xscale));
		p.yre = ypos(status);
		//p.yrr = ypos(status.trn.rmse);
		//p.yrm = ypos(status.trn.mxae);
		//p.testing = status.testing;
		//if (p.testing) {
		//	p.yse = ypos(status);
			//p.ysr = ypos(status.tst.rmse);
			//p.ysm = ypos(status.tst.mxae);
		//}
		return p;
	}

	/**
	 * Calcula la posicion de un valor sobre la coordenada Y
	 */
	private int ypos(double value) {
		if (value <= 1e-10)
			return y0;
		return (int) (y1 - (y0 - y1) * Math.log(value) / (yresc * log10));
	}

	/**
	 * Detecta si un punto se sale de los limites de la grafica
	 */
	private boolean verticalOutOfBounds(XfslGraphPoint p) {
		if (p.yre > y0 || p.yrr > y0 || p.yrm > y0)
			return true;
		if (p.testing && (p.yse > y0 || p.ysr > y0 || p.ysm > y0))
			return true;
		return false;
	}

	/**
	 * Detecta si un punto se sale de los limites de la grafica
	 */
	private boolean horizontalOutOfBounds(XfslGraphPoint p) {
		return (p.x > x1);
	}
}